#ifndef __MEMFS_H_INCLUDED
#define __MEMFS_H_INCLUDED

#include <iostream>
#include "scos.h"

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS 4096

class MemElement 
{  

public:
  friend class Mfs;
  friend class Mshell;

  MemElement();
  ~MemElement();

private:

  //Name of the element (dir or entry)
  char*      elementName;
  
  //content of this entry, NULL if dir
  char*      entryContent;
  
  //reference to subdir, NULL if entry
  MemElement* dirContent;


  //Pointers to the previous/next element of this directory
  MemElement* previousElement;
  MemElement* nextElement;
  
  //Pointer to the directory above this directory
  MemElement* upDir;  
};

class Mfs
{
  friend class Mshell;

 public:
  typedef enum  {MDM_NO_ERROR =0, 
			      MDM_ELEMENT_NOT_FOUND = -1, 
			      MDM_NOT_MOUNTED = -2, 
			      MDM_ALREADY_MOUNTED = -3,
			      MDM_NO_MORE_ELEMENTS = -4 } ManagerErrors;

  typedef enum {MDM_DIR = 1, 
			     MDM_ENTRY = 2} ElementTypes;

  //Constructor for a unmounted directory manager
  Mfs();

  //Mount the directory manager at the root of treeElement
  //Mfs(MemElement* treeElement);

  //Destructor, called when instance is deleted
  ~Mfs();
  
private:

  //Change directory to desired path. f.i. "/", "/dir1/subdir2", "subdir2"
  int ChDir(const char *dirpath);

  //Create a directory with name dirname. f.i."/newdir1" , "/dir1/newdir2"
  // "newdir1/newdir2". So this function never fails (only on memory problems)
  int MkDir(const char *dirpath);

  //Make an entry (make all dirs if necessary) and put value in it.
  int MkEntry  (const char *entrypath, double content);
  int MkEntry  (const char *entrypath, char* content);
  int MkEntry  (const char *entrypath, const char* content)
  	{ return MkEntry(entrypath, ( char* ) content); }

  //Remove element recursively. entries and dirs are accepted
  int RmEntry(const char *entrypath);
  int RmDir(const char *dirpath);

  //Get contents of an entry.
  int                   GetContent(const char *entrypath, char** content);
  template<class T> int GetContent(const char *entrypath, T* content);
  
  //Set the contents of an entry. Optional: create the entry when necessary
  template<class T> 
    int SetContent(const char *entrypath, T content,
		   bool makeEntryIfNecessary=false);
  
  /* Import/Export functions */

  int ImportFromFile(const char *file, bool deleteObsoleteElements=false,
		     bool addNewElements=true);
  int ExportToFile(const char *file, bool deleteObsoleteElements=false,
		   bool addNewElements=true);

  // Compose the path to element loper, by recursive algorithm
  void MakePath(char *path, MemElement* loper);
  // Recursively add updir to the path
  void AddUpDirToPath(char *path, MemElement* loper);

  // Select an element with full path dirpath. element has to be of type isDir
  // gives an error if path is not found.
  int SelectElement(MemElement** element, const char *dirpath, bool isDir);

  // Internal recursive version of above function
  // extras:
  // if onlyName == true, dirpath is only the elementname within 
  // directory *element
  // if createIfNecessary == true, the path is created when not found
  int SelectElement(MemElement** element, const char *dirpath, 
		    bool isDir, bool createIfNecessary, 
		    bool onlyName);

  //Pointer to the root
  MemElement* root;

  //Pointer to the current directory. This pointer could be invalid!!
  //MemElement* currentDir;

  //Copy of the name of the current directory. This is used to get the pointer
  //to the current directory
  //char currentPath[1024];

};

class Serialiser;

class Mshell
{
  friend class Mfs;
public:

  Mshell(Mfs &filesystem, const char *newhomedir = "/");
  Mshell(Mshell &parentshell, const char *newhomedir = "~");
  ~Mshell();

  //Get the current directory name
  const char* pwd() { return currentPath; };

  //Change directory to desired path. f.i. "/", "/dir1/subdir2", "subdir2"
  int ChDir(const char *dirpath);

  //Create a directory with name dirname. f.i."/newdir1" , "/dir1/newdir2"
  // "newdir1/newdir2". So this function never fails (only on memory problems)
  int MkDir(const char *dirpath);

  //Make an entry (make all dirs if necessary) and put value in it.
  //template<class T> int MkEntry  (char *entrypath, T content);
  template<class T>
  int MkEntry(const char *entrypath, T content)
    /*
     * Make an entry (make all dirs if necessary) and put the content in it.
     */
  {
    char fullpath[1024];
    GetAbsolutePath(fullpath, entrypath);
    return fs->MkEntry(fullpath, content);
  }

  //Remove element recursively. entries and dirs are accepted
  int RmEntry(const char *entrypath);
  int RmDir(const char *dirpath);

  //Get contents of an entry.
  template<class T> int GetContent(const char *entrypath, T* content);
  
  //Set the contents of an entry. Optional: create the entry when necessary
  template<class T> 
    int SetContent(const char *entrypath, T content,
		   bool makeEntryIfNecessary=false);

  /*Search functions: */

  // Inits the list of elements of type typeMask contained in the directory
  int FindInitDirectoryElements(const char *dirpath, int typeMask=255);

  // Get the next element (by name) in this directory.
  int FindGetNextElement( char ** const elementname);

  // Rewind the search in this directory
  int FindRewind();

  int Export(Serialiser &serialiser, const char *exportpath);
  int Import(Serialiser &serialiser, const char *importpath);

  /// Copies recursively the contents of the currentdir of this shell, to the currentdir of shell sh.
  int copy(Mshell &sh);

private:

  int copy(Mshell &sh, char *exportpath);
  int GetAbsolutePath(char *abspath, const char* dirpath);
  
private:
  Mfs* fs;

  //path of this shell's homedir
  char *homedir;

  //Pointer to the current directory. This pointer could be invalid!!
  //MemElement* currentDir;

  //Copy of the path to the current directory. This is used to get the pointer
  //to the current directory
  char currentPath[1024];

  /* Find variables */

  // contains the number of found elements
  int numberOfFoundElements;

  //Contains fullpaths to all the found elements
  char* foundElements[ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS];
  
  //Pointer to the next element to be fetched
  int currentElement;
};

class Serialiser
{
  public:
  // constructor
  Serialiser();

  // destructor
  virtual ~Serialiser();

  // export functions, called by Mshell when exporting the memory filesystem
  virtual int StartDir(char *dirname);
  virtual int EndDir(char *dirname);
  virtual int AddEntry(char *entryname, char *content);

  // import functions, called by Mshell
  // the actual serialer should user the shell to create the internal structure
  virtual int Import(Mshell &msh, const char *importpath);
};


class XMLParser : public Serialiser
{
 public:
  XMLParser(std::ostream &newostream);
  XMLParser(std::istream &newistream);
  ~XMLParser();

  // export functions, called by Mshell
  int StartDir(char *dirname);
  int EndDir(char *dirname);
  int AddEntry(char *entryname, char *content);

  // import functions, called by Mshell
  int Import(Mshell &msh, const char *importpath);
  
private:
  enum TokenTypes { TT_NONE=0, TT_UNKNOWN, TT_STARTDIR, TT_ENDDIR, TT_ENTRY};
  int ParseSentence(char *sentence, char *content, int *type);

  //stream that is used to import/export from/to
  std::istream *myistream; 
  std::ostream *myostream; 

  //Remember the current indentation level
  int indentation;
};

template<class T> 
inline int Mfs::SetContent(const char *entrypath, T content,
			      bool makeEntryIfNecessary)
  /*
   * Set the content of entrypath (full path)
   * if makeEntryIfnecessary==true, the path is made first
   */
{
  /* Try to make entry entrypath, this could be a full path */

  MemElement* loper;
  int errCode = SelectElement(&loper,entrypath,false,makeEntryIfNecessary,
			      false);
  if (errCode) return errCode;
  return MkEntry(entrypath,content);
}

template<class T> 
inline int Mfs::GetContent(const char *entrypath, T* content)
  /* 
   * Get the T content of entrypath (a full path)
   * returns an error if element could not be found
   */
{
  MemElement* loper;
  // Try to select entry entrypath, this could be a full path 
  int errCode = SelectElement(&loper,entrypath,false,false,
			      false);
  if (errCode) return errCode;
  //get the content
  *content=(T) atof(loper->entryContent);
  return Mfs::MDM_NO_ERROR;
}

template<class T> 
inline int Mshell::SetContent(const char *entrypath, T content,
			      bool makeEntryIfNecessary)
  /*
   * Set the content of entrypath
   * if makeEntryIfnecessary==true, the path is made first
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, entrypath);
  return fs->SetContent(fullpath, content, makeEntryIfNecessary);
}

template<class T> 
inline int Mshell::GetContent(const char *entrypath, T* content)
  /* 
   * Get the content of entrypath, returns an error if element could not
   * be found
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, entrypath);
  return fs->GetContent(fullpath, content);
}

#endif
